package com.lee.commonerror.code.lock;

import com.lee.commonerror.common.annotation.Wrong;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author ：liylw
 * @date ：Created in 2021/4/12 18:33
 */
@Slf4j
public class DeadLockDemo {

    private static Map<String, Item> items;

    static {
        items = IntStream.rangeClosed(0, 9)
                .mapToObj(i -> new Item("item" + i, 1000, new ReentrantLock()))
                .collect(Collectors.toMap(Item::getName, Function.identity()));
    }


    @Data
    @AllArgsConstructor
    static class Item {
        final String name;
        int remaining = 1000;
        @ToString.Exclude
        ReentrantLock lock = new ReentrantLock();
    }


    private List<Item> createCart() {
        return IntStream.rangeClosed(1, 3)
                .mapToObj(i -> "item" + ThreadLocalRandom.current().nextInt(items.size()))
                .map(name -> items.get(name)).collect(Collectors.toList());
    }


    private boolean createOrder(List<Item> order) {
        //保存获得的锁
        List<ReentrantLock> locks = new ArrayList<>();

        for (Item item : order) {

            try {
                if (item.lock.tryLock(10, TimeUnit.SECONDS)) {
                    locks.add(item.lock);
                } else {
                    locks.forEach(ReentrantLock::unlock);
                    return false;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            order.forEach(item -> item.remaining--);
        } finally {
            locks.forEach(ReentrantLock::unlock);
        }
        return true;
    }

    @Wrong()
    public long wrong() {
        long start = System.currentTimeMillis();

        long count = IntStream.rangeClosed(1, 100).parallel()
                .mapToObj(i -> {
                    List<Item> cart = createCart();
                    return createOrder(cart);
                })
                .filter(result -> result)
                .count();

        log.info("success:{} totalRemaining:{} took:{}ms items:{}",
                count,
                items.entrySet().stream().collect(Collectors.summarizingLong(item -> item.getValue().getRemaining())).getSum(),
                System.currentTimeMillis() - start, items
        );
        return count;
    }


    public static void main(String[] args) {
        DeadLockDemo deadLockDemo = new DeadLockDemo();
        deadLockDemo.wrong();
    }
}
